package com.lilingguo.easyserver.core;

import com.alibaba.fastjson.JSON;
import com.lilingguo.easyserver.pojo.ServiceInfo;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.util.*;

/**
 * EasyServer核心类
 *
 * @Author: 黎铃果
 * @Date: 2020/5/28 16:23
 */
@Log4j2
public class EasyServer {

    /**
     * 用于发送Restful请求
     */
    private final RestTemplate restTemplate;
    /**
     * 本地服务信息
     */
    private final ServiceInfo localServer;
    /**
     * 注册中心的各个接口
     */
    private final String URL_CENTER;
    private final String URL_REGISTER;
    private final String URL_GET_SERVER_LIST;
    private final String URL_TICK;
    private final String URL_CLOSE;
    /**
     * 服务唯一标识
     */
    private String uuid;
    /**
     * 本地服务列表缓存
     */
    private final Map<String, List<ServiceInfo>> serverData;

    public EasyServer(RestTemplate restTemplate, ServiceInfo localServer) {
        this.restTemplate = restTemplate;
        this.localServer = localServer;
        this.URL_CENTER = localServer.getRegisterUrl();
        this.URL_REGISTER = this.URL_CENTER + "/register";
        this.URL_GET_SERVER_LIST = this.URL_CENTER + "/getServiceList";
        this.URL_TICK = this.URL_CENTER + "/heartBeat";
        this.URL_CLOSE = this.URL_CENTER + "/close";
        this.serverData = new HashMap<String, List<ServiceInfo>>();
    }

    /**
     * 返回指定服务的某生产者URL
     *
     * 负载均衡策略：随机
     *
     * @param appName 服务名
     * @return url
     */
    public String getServerUrl(String appName) {
        List<ServiceInfo> list = this.getServerList(appName);
        if (list == null || list.isEmpty()) {
            // empty
            throw new RuntimeException("there is no validate server in EasyServer[" + this.URL_CENTER + "]");
        }
        // random
        int i = new Random().nextInt(list.size());
        ServiceInfo info = list.get(i);
        return "http://" + info.getIp() + ":" + info.getPort();
    }

    /**
     * 返回指定列表
     * @param appName 服务名
     * @return 服务列表
     */
    public List<ServiceInfo> getServerList(String appName) {
        List<ServiceInfo> list = serverData.get(appName);
        if (list != null && !list.isEmpty()) {
            return list;
        }
        return updateServerData(appName);
    }

    /**
     * 更新指定列表
     * @param appName 服务名
     * @return 服务列表
     */
    public List<ServiceInfo> updateServerData(String appName) {
        String url = this.URL_GET_SERVER_LIST + "?appName=" + appName;
        Set set = this.restTemplate.getForObject(url, Set.class);
        if (set == null) {
            return Collections.emptyList();
        }
        List<ServiceInfo> list = new ArrayList<ServiceInfo>();
        for (Object obj : set) {
            String jsonString = JSON.toJSONString(obj);
            ServiceInfo info = JSON.parseObject(jsonString, ServiceInfo.class);
            list.add(info);
        }
        this.serverData.put(appName, list);
        return list;
    }

    /**
     * 更新所有服务列表
     */
    public void updateAllData() {
        if (serverData.isEmpty()) {
            return;
        }
        Set<String> keySet = serverData.keySet();
        for (String appName : keySet) {
            this.updateServerData(appName);
        }
        log.debug("update services -> " + keySet.toString());
    }

    /**
     * 注册服务
     */
    public void register() {
        Date currentDate = new Date();
        this.localServer.setRegisterTime(currentDate);
        this.localServer.setTtl(currentDate.getTime());
        log.info("register service [" + this.localServer.getIdStr() + "] to [" + this.URL_REGISTER + "]");
        try {
            String resp = this.restTemplate.postForObject(this.URL_REGISTER, localServer, String.class);
        } catch (Exception e) {
            log.error("register failed");
        }
    }

    /**
     * 发送心跳，心跳失败则尝试重新注册
     */
    public void heartBeat() {
        log.debug("send heartBeat to [" + this.localServer.getRegisterUrl() + "]");
        String resp = this.restTemplate.postForObject(this.URL_TICK, this.localServer, String.class);
        if (!ServiceInfo.OK.equals(resp)) {
            // error
            log.warn("failed heartBeat, try to register again");
            this.register();
        }
    }

    /**
     * 停止服务（注销）
     */
    public void close() {
        restTemplate.delete(this.URL_CLOSE, this.localServer);
        log.info("service closed");
    }

}
